package qu.sol2bpel.pte;



import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.ParserConfigurationException;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.unify_framework.instances.bpel.BpelCompositeInvokeActivity;
import org.unify_framework.instances.bpel.BpelCorrelation;
import org.unify_framework.instances.bpel.BpelScopeActivity;
import org.xml.sax.SAXException;

import qu.sol2bpel.workflow.DeployXML;
import qu.sol2bpel.workflow.WSDLGenerator;

import qu.sol2bpel.protocol.Protocol;
import qu.sol2bpel.protocol.ProtocolDeclarationListener;
import qu.sol2bpel.protocol.ProtocolFunctionalListener;
import qu.sol2bpel.protocol.ProtocolFunctionalVisitor;
import qu.sol2bpel.protocol.Setup_step;
import qu.sol2bpel.services.ServicesLocalRegistry;
import qu.sol2bpel.sol.grammar.SOL_DeclarativeLexer;
import qu.sol2bpel.sol.grammar.SOL_DeclarativeParser;
import qu.sol2bpel.sol.grammar.SOL_FunctionalLexer;
import qu.sol2bpel.sol.grammar.SOL_FunctionalParser;
import qu.sol2bpel.workflow.Config;
import qu.sol2bpel.workflow.Workflow;

public class PTE {

	Protocol protocol;
	Workflow workflow;

	private int assign_counter = 0;
	private int var_counter = 0;
	private int invoke_counter = 0;
	private int receive_counter = 0;
	private int namespace_counter = 0;
	private ArrayList<Integer> flow_stack= new ArrayList<Integer>();
	private int current_flow = 0;
	private int flow_counter = 0;
	private int curr_scope = 0;
	private int  output = 0;
	/**
	 * @param args
	 * @throws IOException 
	 */
	public void init(String declarative_section, String functional_section) throws IOException
	{
		//Reading the DSL script
		InputStream is = new FileInputStream(declarative_section);
		//Loading the DSL script into the ANTLR stream.
		CharStream cs = new ANTLRInputStream(is);

		//Passing the input to the lexer to create tokens
		SOL_DeclarativeLexer lexer = new SOL_DeclarativeLexer(cs);

		CommonTokenStream tokens = new CommonTokenStream(lexer);

		//Passing the tokens to the parser to create the parse tree. 
		SOL_DeclarativeParser parser = new SOL_DeclarativeParser(tokens);

		//Adding the listener to facilitate walking through parse tree. 
		//parser.addParseListener(new MyGraphBaseListener(g));

		//invoking the parser. 
		ParseTree tree = parser.declarative_section();
		ProtocolDeclarationListener decl_listener = new ProtocolDeclarationListener();
		ParseTreeWalker walker = new ParseTreeWalker();
		walker.walk(decl_listener, tree);

		protocol = decl_listener.getProtocol();

		is = new FileInputStream(functional_section);
		//Loading the DSL script into the ANTLR stream.
		CharStream cs2 = new ANTLRInputStream(is);

		//Passing the input to the lexer to create tokens
		SOL_FunctionalLexer func_lexer = new SOL_FunctionalLexer(cs2);

		CommonTokenStream func_tokens = new CommonTokenStream(func_lexer);

		//Passing the tokens to the parser to create the parse tree. 
		SOL_FunctionalParser func_parser = new SOL_FunctionalParser(func_tokens);


		//Adding the listener to facilitate walking through parse tree. 
		//parser.addParseListener(new MyGraphBaseListener(g));

		//invoking the parser. 
		ParseTree functional_tree = func_parser.functional_section();
		//	ProtocolFunctionalVisitor func_visitor = new ProtocolFunctionalVisitor(protocol);
		//	func_visitor.visit(functional_tree);

		//	protocol = func_visitor.getProtocol();

		ProtocolFunctionalListener func_listener = new ProtocolFunctionalListener(protocol);
		ParseTreeWalker func_walker = new ParseTreeWalker();
		func_walker.walk(func_listener, functional_tree);

		protocol = func_listener.getProtocol();

		protocol.printProtocolMembers();
		//protocol name and setup operations will be set from the functional part

	}

	public void MapSingleStep(Setup_step step) throws ParserConfigurationException, SAXException, IOException
	{
		int chosen_service = 0;
		for(int j=0 ; j < ServicesLocalRegistry.sizeOfServicesList() ; j++)
			if(ServicesLocalRegistry.getService(j).getWSDLFileName().equals(step.getOperation()))
				chosen_service = j;

		String operation = step.getOperation();

		//******HERE I SHOULD MAP step inputs to either protocol input variable or another step output**// 

		String inputs_String ="";

		ArrayList<String> msg_elements = new ArrayList<String>(ServicesLocalRegistry.getServiceElementSchema(chosen_service, operation));
		//SHOULD LOOP HERE FOR THE INPUTS of the STEP and the corresponding inputs in the workflow
		for(int i=0; i<step.getInputs().size(); i++)
		{
			inputs_String = inputs_String.concat("<ws:"+msg_elements.get(i)+">" // look for a way  to get the fields for the inputs only like in put(A1,B1)
					+ step.getInputs().get(i)    //HOW TO GET URL OF THIS RESOURCE??--> get it from the input variable or other workflow variables in other assign activities
					+ "</ws:"+msg_elements.get(i)+">");
		}


		String request_str = "<bpel:literal><ws:"+ServicesLocalRegistry.getServiceOperationElementName(chosen_service, operation) +" xmlns:ws=\""
				+ ServicesLocalRegistry.getServiceTargetNamespace(chosen_service)
				+ "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
				+ "<ws:op_id>"
				+  invoke_counter
				+ "</ws:op_id>"
				+ "<ws:workflow_id>"
				+ workflow.getWf_name()
				+ "</ws:workflow_id>"
				+ inputs_String
				+ "<ws:callback>"
				+ Config.DEPLOYMENT_URL+"/ode/processes/"+ workflow.getWf_name()
				+ "</ws:callback>"
				+ "</ws:"+ServicesLocalRegistry.getServiceOperationElementName(chosen_service, operation)+"></bpel:literal>";


		//System.out.println(inputs_String);
		int input = var_counter;
		workflow.addMessageVariable("var" + input,"ns"+chosen_service+":"+ServicesLocalRegistry.getServiceOperationMessageName(chosen_service, operation)); // reading

		curr_scope += 1;
		workflow.addScope(curr_scope);

		workflow.addLocalPartnerlinks(ServicesLocalRegistry.getServiceOperationName(chosen_service, operation)+"PL"+invoke_counter,
				curr_scope,"tns:"+ServicesLocalRegistry.getServicePortType(chosen_service, operation)+"_"+operation+"_PLT" ,ServicesLocalRegistry.getServicePortType(chosen_service, operation),chosen_service);


		workflow.addAssign("Assign" + assign_counter, request_str,
				"var" + input, curr_scope);



		workflow.addCopyToAssign("Assign" + assign_counter, "var" + input,  "op_id", (new Integer(invoke_counter)).toString(),curr_scope);
		workflow.addCopyVariableToAssign("Assign" + assign_counter, "var" + input,  "workflow_id", "input","tns:workflow_id" ,curr_scope);

		//TODO: modify the operands are set according to the inputs of the step..
		for(int i=0; i<step.getInputs().size(); i++)
		{
			workflow.addCopyToAssign("Assign" + assign_counter, "var" + input, msg_elements.get(i), "\""+step.getInputs().get(i)+"\"",curr_scope);
		}

		// Invoke  webservice
		workflow.addCorrelationSet("SUBJOB_" + invoke_counter,
				"tns:subjob_" + invoke_counter);

		workflow.addInvokeActivity("Invoke" + invoke_counter,
				ServicesLocalRegistry.getServiceOperationName(chosen_service,operation),//	"initiate",
				ServicesLocalRegistry.getServiceOperationName(chosen_service, operation)+"PL" + invoke_counter,
				"ns"+chosen_service+":"+ServicesLocalRegistry.getServicePortType(chosen_service,operation), "var" + input, "SUBJOB_"
						+ invoke_counter, curr_scope);

		output = ++var_counter;
		workflow.addMessageVariable("var" + output, "ns"+chosen_service+":"+ServicesLocalRegistry.getServiceOperationMessageName(chosen_service,"response"));//	exp_wf.addMessageVariable("var" + output, "tns:callbackRequestMessage"+receive_counter);


		workflow.addCallbackActivity("Callback" + receive_counter,
				"callback"+receive_counter, "client",
				"tns:"+workflow.getWf_name(), "var" + output,
				"SUBJOB_" + invoke_counter, curr_scope);

		workflow.connectToScopeStart("Assign" + assign_counter,
				"Scope" + curr_scope);
		workflow.connect("Assign" + assign_counter, "Invoke"
				+ invoke_counter, "Scope" + curr_scope);
		workflow.connect("Invoke" + invoke_counter, "Callback"
				+ receive_counter, "Scope" + curr_scope);
		workflow.connectToScopeEnd("Callback" + receive_counter,
				"Scope" + curr_scope);


		workflow.connectToLastNode("Scope" + curr_scope);
		if (current_flow > 0)
			workflow.connectToFlow(flow_stack.get(current_flow - 1),
					"Scope" + curr_scope);
		else if (current_flow ==0 && flow_stack.size()==0)
			workflow.updateLastNode("Scope" + curr_scope);
		else
		{
			workflow.updateLastNode("Scope" + curr_scope);
			workflow.connectToEnd("Scope" + curr_scope);
		}

		assign_counter++;
		invoke_counter++;
		receive_counter++;
		var_counter++;
	}
	
	public void addSetupSteps() throws ParserConfigurationException, SAXException, IOException
	{
		String operation = "setup"; 
		int chosen_service = 0;
		for(int j=0 ; j < ServicesLocalRegistry.sizeOfServicesList() ; j++)
			if(ServicesLocalRegistry.getService(j).getWSDLFileName().equals(operation))
				chosen_service = j;
	
		
		String services_URLs = "<ws:services_list>";
			
		for(int i=0; i < protocol.getSetup_steps().size() ; i++)
		{
			services_URLs += "<ws:service_name>"+ protocol.getSetup_steps().get(i).getOperation()+"</ws:service_name>";
		}
		
		
		services_URLs += "</ws:services_list>";
		
		String request_str = "<bpel:literal><ws:"+ServicesLocalRegistry.getServiceOperationElementName(chosen_service, operation) +"  xmlns:ws=\""
				+ ServicesLocalRegistry.getServiceTargetNamespace(chosen_service)
				+ "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"			
				+ "<ws:workflow_id>"
				+ workflow.getWf_name()
				+ "</ws:workflow_id>"
				+ services_URLs
				+ "</ws:"+ServicesLocalRegistry.getServiceOperationElementName(chosen_service, operation)+"></bpel:literal>";
		
		workflow.addMessageVariable("setup_var" ,"ns"+chosen_service+":"+ServicesLocalRegistry.getServiceOperationMessageName(chosen_service, operation)); // reading
		workflow.addMessageVariable("setup_output" ,"ns"+chosen_service+":"+ServicesLocalRegistry.getServiceOperationMessageName(chosen_service, operation)); // reading

		String partnerLinkType = "ns"+chosen_service+":"+ServicesLocalRegistry.getServicePortType(chosen_service, operation)+"_"+operation+"_PLT";
		workflow.addPartnerLink("SetupPL",	partnerLinkType,ServicesLocalRegistry.getServicePortType(chosen_service, operation),chosen_service);


		workflow.addAssign("AssignSetup" , request_str,	"setup_var" );
		
		workflow.addTwoWaysInvokeActivity("setupInvoke", "setup", "Setup_PL", partnerLinkType, "setup_var", "setup_output");
		
		workflow.connectToLastNode("AssignSetup");
		workflow.connect("AssignSetup", "setupInvoke");
		
		workflow.updateLastNode("setupInvoke");
	}

	public void startEngine(Config conf)
	{
		ServicesLocalRegistry registry = new ServicesLocalRegistry(conf.SERVICES_DIRECTORY);
		workflow = new Workflow(protocol);
		workflow.setFolder_Path(conf.WORKFLOW_DIRECTORY);
		workflow.setBroker_services_url(conf.DEPLOYMENT_URL);

		try {
			
			workflow.initialize();
			addSetupSteps();
			
			//Map Protocol setup constructs to BPEL workflow elements
			for(int i =0; i< protocol.getSetup_steps().size(); i++)
				MapSingleStep(protocol.getSetup_steps().get(i));
			
			workflow.deploy(Config.DEPLOYMENT_URL);
		
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		//deployWorkflow(conf.DEPLOYMENT_URL);
	}

	public void deployWorkflow(String url)
	{
		workflow.deploy(url);
	}

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub

		Config conf = new Config();


		String declarative_section;  //path to the declaration file for the outsourcing protocol
		String functional_section;   //path to the functional specification file for the outsourcing protocol
		if(args.length==2)
		{
			declarative_section = new String(args[0]);	
			functional_section = new String(args[1]);
		}
		else
		{
			declarative_section = new String("E:\\sol2bpel\\grammar\\testfiles\\Test3.txt");	
			functional_section = new String("E:\\sol2bpel\\grammar\\testfiles\\Test3_func.txt");	
		}

		PTE engine = new PTE();
		engine.init(declarative_section, functional_section);
		engine.startEngine(conf);
	}

}
